import datetime
import random
import time
from logger import log_message, log_to_file
from crawl_every_plan_data_url import load_url, open_account_palt_form
from DrissionPage import SessionPage, ChromiumOptions, SessionOptions, Chromium
from change_bid import change_bid_crawl
import traceback
from wecom_notice import wecom_notice_plan


# 标准消费比例（可改为从JSON或配置文件中读取）
standard_spend_ratios = {
    "0:00": 0, "1:00": 2, "2:00": 3.5, "3:00": 4.5, "4:00": 5.0,
    "5:00": 5.8, "6:00": 6.8, "7:00": 8, "8:00": 10, "9:00": 12,
    "10:00": 15, "11:00": 20, "12:00": 25, "13:00": 30, "14:00": 36,
    "15:00": 44, "16:00": 51, "17:00": 55, "18:00": 59, "19:00": 65,
    "20:00": 71, "21:00": 78, "22:00": 87, "23:00": 95, "24:00": 100
}


def adjust_bid(current_bid, max_bid, min_bid, current_time, accumulated_spend, total_budget, standard_spend_ratios, adjustment=None, curr_ti_user_price=None, curr_ti_user_price_stand=None, result_text=None):

    allow_gap = 0.05  # 允许幅动的区间5%

    hour = current_time.hour
    closest_time_key = f"{hour}:00"
    standard_spend_percentage = standard_spend_ratios.get(closest_time_key, 0)
    standard_spend = total_budget * (standard_spend_percentage / 100.0) # 标准值 = 计划预算 * 标准速率

    # 允许 ±5% 的浮动区间
    lower_bound = standard_spend * (1 - allow_gap)
    upper_bound = standard_spend * (1 + allow_gap)

    adjustment_direction = 0
    if accumulated_spend < lower_bound:  # 过慢
        adjustment_direction = 1
    elif accumulated_spend > upper_bound: # 过快
        adjustment_direction = -1
    else:
        return current_bid, lower_bound, upper_bound

    # 新增逻辑：如果当前 cvr ≥ 标准 cvr，则只能降不能升
    if curr_ti_user_price_stand is not None:
        curr_ti_user_price = float(curr_ti_user_price)
        curr_ti_user_price_stand = float(curr_ti_user_price_stand)

        if curr_ti_user_price >= curr_ti_user_price_stand:
            log_message(result_text, f'当前TI值:{curr_ti_user_price} > 标准值:{curr_ti_user_price_stand}, 进行降价处理', "purple")
            adjustment_direction = -1  # 只能降价

        if curr_ti_user_price == 0.0:
            log_message(result_text, f'当前TI值:{curr_ti_user_price} 为 0, 进行降价处理', "purple")
            adjustment_direction = -1  # 只能降价

    new_bid = current_bid + (adjustment_direction * adjustment)
    new_bid = max(min_bid, min(max_bid, new_bid))

    return new_bid, lower_bound, upper_bound

def reset_all_bids_to_initial(excels, result_text, unit_dict_cookie, account_id):
    """将所有广告单元的出价重置为初始值"""
    for item in excels:
        unit_name = item["单元名称"]
        unit_id = item["单元ID"]
        initial_bid = float(item["今日初始出价"])  # 初始出价（元）

        change_bid_crawl(
            unit_dict_cookie=unit_dict_cookie,
            account_id=account_id,
            unitIds=unit_id,
            eventBid=int(initial_bid * 100),
            result_text=result_text
        )

        log_dict = {
            "重置时间": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
            '单元ID': unit_id,
            '单元名称': unit_name,
            '重置今日初始出价': initial_bid
        }

        time.sleep(1)
        msg = f"🔄 已将 [{unit_name}] 的出价重置为 {initial_bid:.2f}"
        log_message(result_text, msg, "green")
        log_to_file(log_dict)


def wait_until_assign_hour(hour=1, result_text=None):
    """等待到下一个小时的当前分钟（如 15:35 -> 16:35）"""
    now = datetime.datetime.now()
    # 获取当前小时+1的时间
    next_run = now + datetime.timedelta(hours=hour)

    log_message(result_text, f" 下次执行时间: {next_run.strftime('%Y-%m-%d %H:%M')}", "blue")
    total_seconds = int((next_run - now).total_seconds())

    # 开始倒计时
    for remaining in range(total_seconds, 0, -1):
        if not result_text.winfo_exists():
            return

        hours, remainder = divmod(remaining, 3600)
        mins, secs = divmod(remainder, 60)
        time_str = f"{hours:02d}:{mins:02d}:{secs:02d}"
        msg = f" 倒计时：{time_str}"

        log_message(result_text, "\r" + msg, "blue")  # \r 表示覆盖当前行
        time.sleep(1)

    log_message(result_text, "\n 倒计时完成，开始执行任务", "green")

def calculate_next_wakeup(now):
    """计算下一次唤醒时间：当天 8:40 或 明天 8:40"""
    wake_up_time = now.replace(hour=8, minute=40, second=0, microsecond=0)
    if now.time() >= wake_up_time.time():
        wake_up_time += datetime.timedelta(days=1)
    return wake_up_time

def wait_until_tomorrow_8_40(result_text=None):
    now = datetime.datetime.now()
    current_time = now.time()

    # 如果当前时间在 00:00 ~ 08:39 之间，则使用今天的 08:40
    if datetime.time(0, 0) <= current_time < datetime.time(8, 40):
        target_time = datetime.datetime.combine(now.date(), datetime.time(8, 40))
    else:
        tomorrow = now.date() + datetime.timedelta(days=1)
        target_time = datetime.datetime.combine(tomorrow, datetime.time(8, 40))

    total_seconds = int((target_time - now).total_seconds())
    log_message(result_text, f"💤 开始倒计时：等待到 {target_time.strftime('%H:%M')}（共 {total_seconds // 60} 分钟）", "blue")

    for remaining in range(total_seconds, 0, -1):
        if not result_text.winfo_exists():  # 防止GUI关闭后继续写入
            break

        hours, remainder = divmod(remaining, 3600)
        mins, secs = divmod(remainder, 60)
        time_str = f"{hours:02d}:{mins:02d}:{secs:02d}"
        msg = f"⏳ 倒计时：{time_str}"
        log_message(result_text, "\r" + msg, "blue")
        time.sleep(1)

    log_message(result_text, "\n✅ 倒计时完成，开始执行任务\n", "green")

def adjust_bids_periodically(excels, result_text, countdown_log_result_text, stop_event, account_id, adjustment):
    co = ChromiumOptions(read_file=False)  # 不读取文件方式新建配置对象
    so = SessionOptions(read_file=False)
    browser = Chromium(addr_or_opts=co, session_options=so)
    # 打开合作伙伴平台
    create_plan_tab, username, account_name = open_account_palt_form(browser, account_id, result_text)
    unit_dict_cookie = create_plan_tab.cookies(all_info=False).as_dict()
    while not stop_event.is_set():
        try:
            now = datetime.datetime.now()
            str_time = now.strftime('%Y-%m-%d %H:%M:%S')
            current_time = now.time()
            hour, minute = current_time.hour, current_time.minute

            # ✅ 新增逻辑：每天23:40重置所有出价为初始值
            if hour >= 23 and minute <= 59:
                log_message(result_text, f"⏰ {str_time} - 正在执行每日出价重置任务", "blue")
                reset_all_bids_to_initial(excels, result_text, unit_dict_cookie, account_id)
                wait_until_assign_hour(hour=9, result_text=countdown_log_result_text)
                # wait_until_tomorrow_8_40(result_text=countdown_log_result_text)
                log_message(result_text, f"✅ {str_time} - 每日出价重置任务完成\n", "blue")
            elif 8 <= hour < 23 or (hour == 23 and minute < 40):
                dict_data, budget_insufficient_list = load_url(account_id, result_text, unit_dict_cookie) # 爬取所有单元列表数据

                for item in excels:
                    if not stop_event.is_set():
                        ti_user_price_stand = item.get('新增深度种草人群成本', None)

                        unit_name = item["单元名称"]
                        unit_id = item["单元ID"]
                        unit_detail_data = dict_data.get(unit_id, {}) # 根据单元ID拿到单元基本信息

                        if not unit_detail_data:
                            log_message(result_text, f'{str_time} - 当前excel表格中的单元ID:{unit_id}-单元名称:{unit_name}-不存在', "red")
                            continue

                        campaign_id = unit_detail_data.get('campaignId', None) # 计划ID
                        ti_user_price = unit_detail_data.get('tiUserPrice', None) # 新增深度种草人群成本
                        campaign_name = unit_detail_data.get('campaignName', None) # 计划名称
                        campaign_day_budget = unit_detail_data.get('campaignDayBudget', None) # 计划日预算 单位：分
                        current_bid = unit_detail_data.get('eventBid', None) # 当前出价 单位：分
                        accumulated_spend = unit_detail_data.get('fee', None) # 累计消费、需要去计划列表爬取

                        # total_budget = float(item["今日预算"])
                        max_bid = float(item["今日最高出价"])
                        min_bid = float(item["今日初始出价"])

                        new_bid, lower_bound, upper_bound = adjust_bid(float(current_bid / 100), max_bid, min_bid, current_time, float(accumulated_spend), int(campaign_day_budget/100), standard_spend_ratios, adjustment=adjustment, curr_ti_user_price=ti_user_price, curr_ti_user_price_stand=ti_user_price_stand, result_text=result_text)
                        rate_status_str = '过慢' if float(accumulated_spend) < lower_bound else '过快' if float(accumulated_spend) > upper_bound else '符合标准区间'

                        # 符合标准区间跳过调价
                        if rate_status_str == '符合标准区间':
                            ignore_change = f'计划ID:{campaign_id} - 当前消费:{accumulated_spend} - 符合标准区间:{lower_bound} - {upper_bound} - 跳过调价'
                            log_message(result_text, ignore_change, "green")
                            log_to_file(ignore_change)
                            continue

                        # 处理一些提示消息或者构造字典数据
                        log_data, msg = get_msg(accumulated_spend, campaign_day_budget, campaign_id, campaign_name, current_bid, new_bid, rate_status_str, lower_bound, upper_bound, adjustment, min_bid, max_bid)
                        log_message(result_text, msg, "green")

                        if ti_user_price_stand is not None:
                            log_message(result_text, f'excel表格中存在【新增深度种草人群成本】标准值,新增调价规则--低于标准正常调价, 高于标准值只进行降价处理, 当前调价算法无视快慢状态', "purple")

                        log_to_file(log_data)

                        # 正式用爬虫进行调价
                        change_bid_crawl(unit_dict_cookie=unit_dict_cookie, account_id=account_id, unitIds=unit_id, eventBid=new_bid*100, result_text=result_text)   # 改价
                        # change_bid_crawl(unit_dict_cookie=unit_dict_cookie, account_id=account_id, unitIds=257593742, eventBid=162, result_text=result_text)
                        time.sleep(random.randint(1, 5))
                        log_message(result_text, '-'*80)

                # TODO 企微发送通知
                if budget_insufficient_list:
                    content = f"⚠️{account_name} -【计划预算不足提醒 - 共{len(budget_insufficient_list)} 条】 @{username} \n\n"

                    for index, insufficient_plan in enumerate(budget_insufficient_list):
                        campaign_id = insufficient_plan.get('campaign_id', '未知')
                        campaign_name = insufficient_plan.get('campaign_name', '未知')
                        campaign_day_budget = insufficient_plan.get('campaign_day_budget', 0)  # 分 → 元
                        accumulated_spend = insufficient_plan.get('accumulated_spend', 0)
                        # current_bid_crawl = insufficient_plan.get('current_bid', 0)

                        # 转换单位为元并保留两位小数
                        budget_yuan = f"{campaign_day_budget / 100:.2f}" if campaign_day_budget else "未知"
                        # current_bid_yuan = f"{current_bid_crawl / 100:.2f}" if current_bid_crawl else "未知"

                        plan_info = (
                            f"📌 {index + 1}.计划ID: {campaign_id} -【{campaign_name}】- "
                            f" 日预算: {budget_yuan} 元 - "
                            f" 已消费: {accumulated_spend} 元 \n"
                        )
                        content += plan_info
                    # 发送企微通知 TODO 上线打开注释
                    wecom_notice_plan(content=content, result_text=result_text)
                # 等待下一小时整点
                # time.sleep(5)
                wait_until_assign_hour(hour=1, result_text=countdown_log_result_text)
            else:
                wait_until_assign_hour(hour=9, result_text=countdown_log_result_text)
        except Exception as e:
            error_msg = f"❌ {str_time} - 程序发生异常:\n{traceback.format_exc()}"
            log_message(result_text, error_msg, "red")
            time.sleep(360)  # 报错后等待6分钟再继续尝试

            # # ✅ 添加倒计时逻辑
            # total_seconds = 3600
            # for remaining in range(total_seconds, 0, -1):
            #     if stop_event.is_set():
            #         break
            #     mins, secs = divmod(remaining, 60)
            #     time_str = f"{mins:02d}:{secs:02d}"
            #     msg = f"⏳ 下次调整倒计时：{time_str}"
            #     log_message(result_text, msg, "blue")  # 只显示最新一条，避免刷屏
            #     log_to_file(f"⏳ 下次调整将在 {time_str} 后执行")
            #     time.sleep(1)
            # else:
            #     continue  # 正常结束倒计时后继续循环


def get_msg(accumulated_spend, campaign_day_budget, campaign_id, campaign_name, current_bid, new_bid, rate_status_str, lower_bound, upper_bound, adjustment, min_bid, max_bid):
    msg = (
        f"{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | "
        f"计划ID: {campaign_id} | "
        f"计划名称: {campaign_name}\n"
        f"{'当前计划预算':<8}: {int(campaign_day_budget / 100):.2f} | "
        f"{'当前出价':<8}: {current_bid / 100:.2f} | "
        f"{'最低出价':<6}: {min_bid} | "
        f"{'最高出价':<6}: {max_bid} | "
        f"{'新出价':<8}: {new_bid:.2f}\n"
        f"{'当前消费':<8}: {accumulated_spend} | "
        f"{'标准区间':<8}: {lower_bound:.2f} - {upper_bound:.2f} | "
        f"{'消费速度':<6}: {rate_status_str} | "
        f"{'调整幅度':<6}: {adjustment}"
    )
    log_data = {
        "时间": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        "计划ID": campaign_id,
        "计划名称": campaign_name,
        "当前计划预算": f'{int(campaign_day_budget / 100):.2f}',
        "当前出价": f'{current_bid / 100:.2f}',
        "最低出价": f'{min_bid}',
        "最高出价": f'{max_bid}',
        "新出价": f'{new_bid:.2f}',
        "当前消费": accumulated_spend,
        "标准区间": f'{lower_bound:.2f} - {upper_bound:.2f}',
        "消费速度": rate_status_str,
        '调整幅度': adjustment
    }
    return log_data, msg
